"""
可视化模块
"""

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.patches import Circle, Arrow, Wedge
from typing import List, Dict, Any, Optional, Tuple
import matplotlib.font_manager as fm
import platform
from usv_boids.core.simulation import SimulationEngine


# 配置中文字体
def configure_chinese_font():
    system = platform.system()
    
    if system == 'Windows':
        font_names = ['Microsoft YaHei', 'SimHei', 'SimSun', 'Arial Unicode MS']
    elif system == 'Darwin':  # macOS
        font_names = ['PingFang SC', 'Songti SC', 'STHeiti', 'Heiti SC', 'Arial Unicode MS']
    else:  # Linux and others
        font_names = ['WenQuanYi Micro Hei', 'WenQuanYi Zen Hei', 'Droid Sans Fallback', 'Noto Sans CJK SC']
    
    # 尝试设置字体
    font_found = False
    for font_name in font_names:
        try:
            if any(font_name.lower() in f.name.lower() for f in fm.fontManager.ttflist):
                plt.rcParams['font.family'] = font_name
                plt.rcParams['axes.unicode_minus'] = False
                font_found = True
                break
        except:
            continue
    
    # 默认字体
    if not font_found:
        try:
            plt.rcParams['font.sans-serif'] = ['DejaVu Sans'] + plt.rcParams['font.sans-serif']
        except:
            pass


# 配置字体
configure_chinese_font()


class Visualizer:
    def __init__(self, sim: SimulationEngine, figsize: Tuple[int, int] = (10, 8),
                 show_metrics: bool = True, plot_interval: int = 5):
        self.sim = sim
        self.show_metrics=show_metrics
        self.plot_interval = plot_interval
        self.frame_count = 0
        
        # 创建图形和子图
        if show_metrics:
            self.fig, (self.ax, self.ax_metrics) = plt.subplots(1, 2, figsize=figsize)
            self.ax_metrics.set_xlabel('仿真时间')
            self.ax_metrics.set_ylabel('指标值')
            self.ax_metrics.set_title('围捕效果指标')
            self.ax_metrics.grid(True)
            
            # 初始化线条
            self.line_dist, = self.ax_metrics.plot([], [], 'b-', label='平均距离')
            self.line_surround, = self.ax_metrics.plot([], [], 'r-', label='围捕比例')
            self.ax_metrics.legend()
        else:
            self.fig, self.ax = plt.subplots(figsize=figsize)
            
        # 配置主图
        self.ax.set_xlim(0, self.sim.env.width)
        self.ax.set_ylim(0, self.sim.env.height)
        self.ax.set_aspect('equal')
        self.ax.set_title('无人艇群围捕仿真')
        self.ax.grid(True, linestyle='--', alpha=0.7)
        
        # 创建对象集合
        self.usv_artists = []
        self.target_artists = []
        self.vector_artists = []
        self.perception_circles = []
        
        # 初始化对象
        self._init_artists()
        
    def _init_artists(self):
        # 为每个USV创建
        for usv in self.sim.usvs:
            # 创建USV图形
            triangle = self.ax.plot([], [], 'b-')[0]
            # 创建速度向量
            vector = self.ax.plot([], [], 'g-', alpha=0.5)[0]
            # 创建感知圈
            circle = Circle((0, 0), usv.percept_radius, fill=False, alpha=0.2, color='b')
            
            self.ax.add_patch(circle)
            self.usv_artists.append(triangle)
            self.vector_artists.append(vector)
            self.perception_circles.append(circle)
            
        # 为每个目标创建
        for target in self.sim.targets:
            # 目标圆形
            circle = Circle((0, 0), target.size, color='r', alpha=0.7)
            
            self.ax.add_patch(circle)
            self.target_artists.append(circle)
            
    def _update_artists(self):
        # 更新USV
        for i, usv in enumerate(self.sim.usvs):
            # 计算三角形顶点
            if np.linalg.norm(usv.velocity) > 0:
                angle = np.arctan2(usv.velocity[1], usv.velocity[0])
            else:
                angle = 0
                
            # 三角形尺寸
            size = usv.size * 2
            
            # 三角形顶点
            x0, y0 = usv.position
            x1 = x0 + size * np.cos(angle)
            y1 = y0 + size * np.sin(angle)
            x2 = x0 + size * 0.5 * np.cos(angle + 2.5)
            y2 = y0 + size * 0.5 * np.sin(angle + 2.5)
            x3 = x0 + size * 0.5 * np.cos(angle - 2.5)
            y3 = y0 + size * 0.5 * np.sin(angle - 2.5)
            
            # 更新三角形
            self.usv_artists[i].set_data([x1, x2, x3, x1], [y1, y2, y3, y1])
            
            # 更新速度向量
            self.vector_artists[i].set_data(
                [x0, x0 + usv.velocity[0] * 2],
                [y0, y0 + usv.velocity[1] * 2]
            )
            
            # 更新感知圈
            self.perception_circles[i].center = usv.position
            
        # 更新目标
        for i, target in enumerate(self.sim.targets):
            self.target_artists[i].center = target.position
            
    def _update_metrics_plot(self):
        if not self.show_metrics or self.frame_count % self.plot_interval != 0:
            return
            
        metrics = self.sim.metrics
        if not metrics['avg_distance_to_target'] or not metrics['target_surrounded_ratio']:
            return
        
        # 使用相同长度
        n_points = min(len(metrics['avg_distance_to_target']), len(metrics['target_surrounded_ratio']))
        if n_points == 0:
            return
            
        times = np.linspace(0, self.sim.time, n_points)
        
        # 确保长度一致
        dists = metrics['avg_distance_to_target'][:n_points]
        surrounds = metrics['target_surrounded_ratio'][:n_points]
        
        # 绘制距离
        self.line_dist.set_data(times, dists)
            
        # 绘制围捕比例
        self.line_surround.set_data(times, surrounds)
            
        # 更新范围
        self.ax_metrics.set_xlim(0, max(times) if len(times) > 0 else 1.0)
        
        max_dist = max(dists) if dists else 1.0
        self.ax_metrics.set_ylim(0, max(1.0, max_dist))
            
    def update(self, frame):
        # 更新状态
        self.sim.update(dt=0.1)
        
        # 更新可视元素
        self._update_artists()
        
        # 更新指标
        self._update_metrics_plot()
        
        self.frame_count += 1
        
        # 返回需更新的对象
        all_artists = self.usv_artists + self.target_artists + self.vector_artists + self.perception_circles
        if self.show_metrics:
            all_artists.extend([self.line_dist, self.line_surround])
            
        return all_artists
        
    def animate(self, frames: int = 200, interval: int = 50):
        anim = animation.FuncAnimation(
            self.fig, self.update, frames=frames, interval=interval, blit=True
        )
        plt.tight_layout()
        plt.show()
        
        return anim
    
    def save_animation(self, filename: str, frames: int = 200, fps: int = 30):
        anim = animation.FuncAnimation(
            self.fig, self.update, frames=frames, blit=True
        )
        
        anim.save(filename, writer='ffmpeg', fps=fps)
        print(f"动画已保存到：{filename}")


class Plotter:
    """
    数据可视化类，用于绘制统计图表和生成报告
    """
    
    @staticmethod
    def plot_metrics(sim: SimulationEngine, save_path: Optional[str] = None):
        """
        绘制仿真指标图表
        
        :param sim: 仿真引擎实例
        :param save_path: 保存路径，如不指定则显示图表
        """
        metrics = sim.metrics
        
        # 确保有足够的数据
        if not metrics['avg_distance_to_target'] or not metrics['target_surrounded_ratio']:
            print("没有足够的指标数据用于绘图")
            return
            
        # 使用相同长度的数组
        n_points = min(
            len(metrics['avg_distance_to_target']), 
            len(metrics['target_surrounded_ratio']),
            len(metrics['usv_clustering']) if metrics['usv_clustering'] else 0
        )
        
        if n_points == 0:
            print("没有足够的指标数据用于绘图")
            return
        
        # 创建图表
        fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 12), sharex=True)
        
        # 时间轴
        times = np.linspace(0, sim.time, n_points)
        
        # 绘制平均距离
        ax1.plot(times, metrics['avg_distance_to_target'][:n_points], 'b-')
        ax1.set_ylabel('平均距离')
        ax1.set_title('USV到目标的平均距离')
        ax1.grid(True)
        
        # 绘制围捕比例
        ax2.plot(times, metrics['target_surrounded_ratio'][:n_points], 'r-')
        ax2.set_ylabel('围捕比例')
        ax2.set_title('目标被围捕的完整度')
        ax2.grid(True)
        
        # 绘制USV聚集程度
        if metrics['usv_clustering']:
            ax3.plot(times, metrics['usv_clustering'][:n_points], 'g-')
            ax3.set_xlabel('仿真时间')
            ax3.set_ylabel('USV间平均距离')
            ax3.set_title('USV集群聚集程度')
            ax3.grid(True)
        
        plt.tight_layout()
        
        if save_path:
            plt.savefig(save_path)
            print(f"指标图表已保存到：{save_path}")
        else:
            plt.show()
            
    @staticmethod
    def generate_report(sim: SimulationEngine) -> str:
        stats = sim.get_stats()
        
        # 构建报告
        report = [
            "=== 无人艇围捕仿真报告 ===",
            f"仿真时长: {stats['time']:.2f} 秒",
            f"无人艇数量: {stats['usv_count']}",
            f"目标数量: {stats['target_count']}",
            ""
        ]
        
        # 添加围捕指标
        if 'current_avg_distance' in stats:
            report.append(f"当前平均距离: {stats['current_avg_distance']:.2f}")
            
        if 'current_surrounded_ratio' in stats:
            report.append(f"当前围捕比例: {stats['current_surrounded_ratio']:.2f}")
            
        if 'targets_fully_surrounded' in stats:
            report.append(f"完全被围捕的目标数: {stats['targets_fully_surrounded']} / {stats['target_count']}")
            
        # 趋势分析
        if stats['metrics']['avg_distance_to_target'] and len(stats['metrics']['avg_distance_to_target']) > 10:
            recent=stats['metrics']['avg_distance_to_target'][-10:]
            previous=stats['metrics']['avg_distance_to_target'][-20:-10] if len(stats['metrics']['avg_distance_to_target']) >= 20 else []
            
            if previous and recent:
                trend = np.mean(recent) - np.mean(previous)
                
                report.append("")
                if trend < -0.1:
                    report.append("距离趋势: ↓ 正在接近目标")
                elif trend > 0.1:
                    report.append("距离趋势: ↑ 正在远离目标")
                else:
                    report.append("距离趋势: → 保持稳定")
                    
        return "\n".join(report) 